//
//  ViewController.m
//  Test
//
//  Created by 杨德龙 on 17/7/21.
//  Copyright © 2017年 杨德龙. All rights reserved.
/*
    1.0  对象收到消息后,究竟该调用哪个方法则完全于运行期决定,甚至可以在运行期改变.
         这些特性使得Objective-C 成为一门真正的动态语言
 
    2.0 我们改变黑盒方法等的执行  测试
 Method originalMethod=class_getInstanceMethod([NSString class], @selector(lowercaseString));
 Method swappedMethod=class_getInstanceMethod([NSString class], @selector(uppercaseString));
 method_exchangeImplementations(originalMethod, swappedMethod);
        这样执行后 所有的 调用不用是在 本viewController 还是在 下个viewController 中 方法的执行都发生了改变
        这
    3.0   
 
 */

#import "ViewController.h"
#import "EOCPerson.h"
#import "EOCEmployer.h"
#import "EOCBitMove.h"
#import "EOCEmployee.h"
#import <objc/runtime.h>
#import "EOCAutoDictionary.h"
#import "EOCCompareAutoDictionary.h"    //   对比
#import "EOCSmithPerson.h"   // 对比
#import "NSString+EOCMyAdditions.h" 
#import "EOCSoundPlayer.h"    //
#import <AudioToolbox/AudioToolbox.h>    // 引进 播放音频的 toolBox
#import "EOCSquare.h"   //  正方形的square
#import "EOCPointOfInterest.h"   //


static void *EOCMyAlertViewKey="EOCMyAlertViewKey";

enum EOCConnectionState{
    EOCConnectionStateDisconnected,
    EOCConnectionStateConnecting,
    EOCConnectionStateConnected,
};

typedef enum EOCConnectionState EOCConnectionState; //  加上这句可以随处使用

enum EOCNewConnectionState{
    EOCNewConnectionStateDisconnected, // 使用默认的值 从 0 开始  后面的会递增
    EOCNewConnectionStateConnecting,
    EOCNewConnectionStateConnected,
};

enum IntegerState:NSInteger{
    IntegerStateOne=1,   // 不使用默认的 0 开始  后面的会递增
    IntegerStateTwo,
    IntegerStateThree,
};

// 这种我们可以 使用按位 或操作符号 得到多个选项  通过按为与运算 判断 某个选项是否设置  0 除外
enum EOCViewAutoResizing{
     EOCViewAutoResizingNone=0,
     EOCViewAutoResizingTop=1<<0,
     EOCViewAutoResizingLeft=1<<1,
     EOCViewAutoResizingRight=1<<2,
};

typedef enum EOCViewAutoResizing EOCViewAutoResizing;


@interface ViewController ()<UIAlertViewDelegate,EOCSoundPlayerDelegate>

@end

/*
 实际上 你定义两个这样的 completion 函数 连 编译都过不了 Xcode 还是作出了优化 但是命名方式还是得规范
 比如  EOCSoundPlayer  中命名为  EOCSoundPlayerCompletion 函数
 但是如果 EOCSoundPlayer  打包成.a 文件给第三方使用的话 会造成 一直编译错误!!!
 请对比下面两端
 */
void completionTwo(SystemSoundID ssID,void *clientData){
    
    // AudioServicesRemoveSystemSoundCompletion (ssID);
    // 报错
    /*
     !!!!!  按照书上的敲法这样 敲失败
     EOCSoundPlayer *player=(__bridge EOCSoundPlayer *)clientData;  // 实际这个clientData  并不是 EOCSoundPlayer
     NSLog(@"client data is %@",player);
     //    EOCSoundPlayer *player=self;  CAlayer 类型
     if ([player.delegate respondsToSelector:@selector(soundPlayerDidFinish:)]) {
     [player.delegate soundPlayerDidFinish:player];
     }
     */
    
    //    //  !!  下面是 笔者修改 好的
    //    if (_soundPlayer&&[_soundPlayer.delegate respondsToSelector:@selector(soundPlayerDidFinish:)]) {
    //        [_soundPlayer.delegate soundPlayerDidFinish:_soundPlayer];
    //    }
    
    NSLog(@"view controller  completion");
}


//void completion(SystemSoundID ssID,void *clientData){
//    
//    // AudioServicesRemoveSystemSoundCompletion (ssID);
//    // 报错
//    /*
//     !!!!!  按照书上的敲法这样 敲失败
//     EOCSoundPlayer *player=(__bridge EOCSoundPlayer *)clientData;  // 实际这个clientData  并不是 EOCSoundPlayer
//     NSLog(@"client data is %@",player);
//     //    EOCSoundPlayer *player=self;  CAlayer 类型
//     if ([player.delegate respondsToSelector:@selector(soundPlayerDidFinish:)]) {
//     [player.delegate soundPlayerDidFinish:player];
//     }
//     */
//    
//    //    //  !!  下面是 笔者修改 好的
//    //    if (_soundPlayer&&[_soundPlayer.delegate respondsToSelector:@selector(soundPlayerDidFinish:)]) {
//    //        [_soundPlayer.delegate soundPlayerDidFinish:_soundPlayer];
//    //    }
//    
//    NSLog(@"view controller  completion");
//}
//


@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // Do any additional setup after loading the view, typically from a nib.
//    [self useNumber];
//    //
//    [self testBitMove];
//    
//    //
//    [self userEnum];
//    
//    //
//    [self testEOCMutableDic];
    
    //  我们 在某个方法中更改了 之后 整个的都变了
//    [self stringMethodExchangeSecond];
//    //
//    [self stringMethodExchange];
//    //
//    [self stringMethodExchangeSecond];
    
    //
//    [self testSetter];
    
    //
//    [self testClassCluter];
    
//    [self testDeadCycleIfNotChange];
    
    //
//    [self testDeadCycleChange];
    
    // search Class Info
//    [self searchClassInfoInherit];
    
//    [self testDumplicatedCompletion];
    
//    [self testSquare];
    
    // 使用
//    [self poDescription];
    
    //
    [self userImmutableObj];
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)firstClick:(id)sender
{
    [self useArray];
}

#pragma mark ----  测试一下位置移动运算 在32位 和 64位的机器

// 实验的结果是不论在 32位数还是 64位上的结果也是一样的
- (void)testBitMove
{
    EOCBitMove *bitMover=[[EOCBitMove alloc]init];
    int  i=1;
    [bitMover moveIntNumber:i leftBit:2]; // 4
    int j=0x40000000;
    [bitMover moveIntNumber:j leftBit:1];  // -2147483648  符号位溢出
    
    int m=1,n=0x80000000;
    [bitMover moveIntNumber:m leftBit:33];  //2
    [bitMover moveIntNumber:n leftBit:33]; // 0
    
    //
    int k=0x80000000;
    [bitMover moveIntNumber:k rightBit:1];
    int v=0b10100110;
    [bitMover moveIntNumber:v rightBit:1];// 83
    
    //
    unsigned char c=3;
    char d=3;
    char e=-3;
    [bitMover moveIntNumber:c leftBit:1]; // 6  都是补上0
    [bitMover moveIntNumber:c rightBit:1]; //1
    [bitMover moveIntNumber:d leftBit:1];  // 6  // 也是补 0
    [bitMover moveIntNumber:d rightBit:1]; //1
    [bitMover moveIntNumber:e leftBit:1];  //-6  // 补1
    [bitMover moveIntNumber:e rightBit:1]; // -2
    
    // 程序执行的时候，操作的是数值的编码表示，也就是数值在内存中的二进制表示。比如说，程序取-3的时候，就去取11111101。 注意

}



#pragma mark ----  Effective UserGuideLines
// 使用 避免嵌套 头文件
- (void)forwardDeclarations
{
    EOCPerson *person=[[EOCPerson alloc]init];
    person.firstName=@"first";
    person.lastName=@"firstLaset";
    
    CustomStruct cs;
    cs.size=CGSizeMake(0, 20);
    cs.origin=CGPointMake(0, 0);
    
    EOCEmployer *em=[[EOCEmployer alloc]init];
    [em addEmployee:person];
    [em removeEmployee:person];
    
    NSLog(@"cs size is %@, ori is: %@",NSStringFromCGSize(cs.size),NSStringFromCGPoint(cs.origin));
}


//
/*
    使用字面数值  注意使用字面量语法都 必须是foundation 框架中的 如果使用自定义的对象
    无法使用字面量语法创建对象. 必须使用 非字面量语法
 */
- (void)useNumber
{
    NSNumber *someNumber=[NSNumber numberWithInt:1]; // 这种我们不推荐使用
    
    NSNumber *intNumber=@1; // 这种简洁的方式 使用
    NSNumber *floatNumber=@2.5f;
    NSNumber *doubleNumber=@3.1415936;
    NSNumber *boolNumber=@YES;
    NSNumber *charNumber=@'a';
    int x=5;
    float y=6.32f;
    NSNumber *expressionNumber=@(x*y);
    
    NSLog(@"expression number is %@",expressionNumber);
}

- (void)useArray
{
    NSArray *animals=[NSArray arrayWithObjects:@"cat",@"dog",@"mouse",@"badger",nil];  // NO
    NSArray *animals_tw=@[@"cat",@"dog",@"mouse",@"badger"]; // YES
    NSString *dog=[animals objectAtIndex:1];
    NSString *dog_two=animals[1];  // 如果 数值下标中元素为nil 会跑出异常  这样方便发现
    
    id  obj1=@"obj1";
    id  obj2=nil;
    id  obj3=@"obj2";
    NSArray *arrayA=[NSArray arrayWithObjects:obj1,obj2,obj3,nil]; //  obj1
    // 实际上  arrayA 中只有含有一个obj1  因为到obj2 是nil  不在插入元素
    NSArray *arrayB=@[obj1,obj2,obj3]; // 出现崩溃  方便发现 其实有很多元素没插入
    
}

- (void)userDic
{
    NSDictionary *dic=[NSDictionary dictionaryWithObjectsAndKeys:@"obj",@"key",nil];
    NSDictionary *dic_two=@{@"key":@"obj"};
    
    // 这两种都会爆出nil的 异常
    NSString *value=dic[@"key"];
    NSLog(@"value is %@",value);
}

- (void)mutableAdd
{
    // 使用字面量语法 创建 可变的 数组
    NSMutableArray *mutableArray=[@[@"J",@"K"] mutableCopy];
    [mutableArray addObject:@"DLLong"];
}


#pragma mark --- tips: 使用枚举表示状态,选项,状态码

- (void)userEnum
{
    // 默认从0  开始
    EOCConnectionState state=EOCConnectionStateConnected;  // 使用了typedef 后可以不用声明 enum
    enum EOCNewConnectionState newState=EOCNewConnectionStateConnecting;
    
    
    //  注意这个是按位 或运算 不是  bool 运算
    EOCViewAutoResizing autoResizing=EOCViewAutoResizingTop|EOCViewAutoResizingLeft;
    NSLog(@"auto Resizing  is %d",autoResizing);

    if (autoResizing&EOCViewAutoResizingLeft)
    {
        // EOCViewAutoResizingLeft is set
        // EOCViewAutoResizingNone 除外因为  这个没法玩
        NSLog(@"EOCViewAutoResizingLeft  在autoResizing 中 ");
    }
    
    
}

#pragma mark ---- 测试类族
- (void)testClassCluter
{
    //  !!! 注意这个employee 对象并不是EOCEmployee class  而是他的子类的 实例
    // 这是类族模式的 区别 和一般的 工厂模式 更能掩盖细节
    EOCEmployee *employee=[EOCEmployee employeeWithType:EOCEmployeeTypeDeveloper];
    BOOL isEmployee=[employee isKindOfClass:[EOCEmployee class]];  // 属于某个类或者他的子类 检测是否在一个类族中的方法
    BOOL isMemberEmployee=[employee isMemberOfClass:[EOCEmployee class]]; // 某个类的实例
    
    if (isEmployee) {
        NSLog(@"emplpoyee class");
    }else{
        NSLog(@"not employee class");
    }
    
    if (isMemberEmployee) {
        NSLog(@"is istance of employee");
    }else{
        NSLog(@" is not instance of employee");
    }
    
    //
    id  maybeAnArray=@[@1,@"123"];
    
    if ([maybeAnArray class]==[NSArray class]) {
        NSLog(@"maybe  an array");
    }else{
        NSLog(@"this not  an array ....");
        Class cl=[maybeAnArray class];
        
        NSLog(@"class obj is: %@",cl);
        // 显示的是 __NSArrayl 类族
    }
    
    if ([maybeAnArray isKindOfClass:[NSArray class]]) { // 是这个类族的部分
        NSLog(@"is array cluter");
    }
    
    NSArray *arr=@[@1,@"123"];
    if ([arr class]==[NSArray class]) {
        NSLog(@"arr is arr");
       
    }
    Class arrClass=[NSArray class];
    NSLog(@"class is %@",arrClass);
    
    if ([maybeAnArray isMemberOfClass:[NSArray class]]) {
        NSLog(@"YES  "); //  都没有进来
    }
    
    if ([arr isMemberOfClass:[NSArray class]]) {
        NSLog(@"YES TWO ");
    }
    
    // 从这些实验我们可以窥探 苹果对于 NSArray  这个类是基于一个类族的  定义 实例的并不是简单的 NSArray的实例
    // 很有可能是别的类型 
}

#pragma mark ----   关联对象

/*
    最佳的方法是创建一个自定义的UIAlertView 的子类在子类中 写入Block 的回调
 */
- (void)associatedObject
{
    // 不同的 alertView 关联不同的block 这样在
    UIAlertView *alertView=[[UIAlertView alloc]initWithTitle:@"title" message:@"message" delegate:self cancelButtonTitle:@"cancle" otherButtonTitles:@"comfirm", nil];
    void (^block)(NSInteger)=^(NSInteger buttonIndex){
        if (buttonIndex==0) {
            [self doCancle];
        }else{
            [self doContinue];
        }
    };
    
    objc_setAssociatedObject(alertView, EOCMyAlertViewKey, block, OBJC_ASSOCIATION_COPY);
    
    [alertView show];
    
}

- (void)doCancle
{
    NSLog(@"cancle click");
}

- (void)doContinue
{
    NSLog(@"do continue");
}

#pragma mark ----  UIAlertView  Delegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    void(^block)(NSInteger)=objc_getAssociatedObject(alertView, EOCMyAlertViewKey);
    if (block) {
        block(buttonIndex);
    }
}



- (IBAction)testAssociatedClick:(id)sender
{
    [self associatedObject];
    
    
    
}

#pragma mark ----  objc_msgSend 的使用   
- (void)testEOCMutableDic
{
    EOCAutoDictionary *dict=[EOCAutoDictionary new];
    dict.date=[NSDate dateWithTimeIntervalSince1970:475372800];
    NSLog(@"dict.date = %@",dict.date);
    // output: dict.date= 1985-01-24 00:00:00 +0000
    
    /*
     dynamic  本身表示需要动态的确定和添加 
     EOCCompareAutoDictionary *compareDic=[EOCCompareAutoDictionary new];
     compareDic.date=[NSDate dateWithTimeIntervalSince1970:475372800];
     NSLog(@"dict.date = %@",compareDic.date);
      对比就可以发现 这个 dynamic  如果没有写setter 和 getter 方法 就会出现下面的报错 对比可以看 问题
      -[EOCCompareAutoDictionary setDate:]: unrecognized selector sent to instance 0x174015b20
     */
   
}


#pragma mark ----  运行时 方法互换 

/**
 改变方法一
 */
- (void)stringMethodExchange
{
    //
    Method originalMethod=class_getInstanceMethod([NSString class], @selector(lowercaseString));
    Method swappedMethod=class_getInstanceMethod([NSString class], @selector(uppercaseString));
    method_exchangeImplementations(originalMethod, swappedMethod);
    NSString *string=@"ThIs iS tHe StRiNg";
    NSString *lowercaseString=[string lowercaseString];
    NSLog(@"lowercaseString = %@",lowercaseString);
    
    NSString *uppercaseString=[string uppercaseString];
    NSLog(@"uppercaseString = %@",uppercaseString);
    
}


/**
 对比第一个 string
 */
- (void)stringMethodExchangeSecond
{
    //
    NSString *string=@"ThIs iS tHe StRiNg";
    NSString *lowercaseString=[string lowercaseString];
    NSLog(@"lowercaseString = %@",lowercaseString);
    
    NSString *uppercaseString=[string uppercaseString];
    NSLog(@"uppercaseString = %@",uppercaseString);
}

#pragma mark ----  运行时  我们测试是否陷入死循环


/**
 检测是否陷入死循环  这个陷入了死循环
 */
- (void)testDeadCycleIfNotChange
{
     NSString *string=@"ThIs iS tHe StRiNg";
    [string eoc_myLowercaseString];
    
}

- (void)testDeadCycleChange
{
    Method originalMethod=class_getInstanceMethod([NSString class], @selector(lowercaseString));
    Method swappedMethod=class_getInstanceMethod([NSString class], @selector(eoc_myLowercaseString));
    
    method_exchangeImplementations(originalMethod, swappedMethod);
    
    NSString *string=@"ThIs iS tHe StRiNg";
    NSString *change=[string eoc_myLowercaseString]; // 这个并没有起任何的作用 .....
    NSLog(@"change is %@",change); // 但是结果变了 this is the string
    
    //交换后 eoc_myLowercaseString 实际执行的是原来的 lowercaseString  所以 结果变了
    // lowercaseString  实际执行的是 lowercaseString  不会陷入死循环
    
    // eoc_myLowercaseString 替换后 再调用bi
    
//    [string lowercaseString]; // 这句起了作用
    
    
}



#pragma makr  ----  我们测试 setter 和getter 方法

- (void)testSetter
{
//    EOCSmithPerson *smithPerson=[[EOCSmithPerson alloc]init];
//    smithPerson.firstName=@"Jason";
//    smithPerson.lastName=@"Jason";
    
    //  EOCPerson 并没有写 setFirstName 的方法 但是依然出现了crash 是因为调用了子类 子类 EOCSmithPerson
    //  重写了这个方法
    EOCPerson *smithPerson=[[EOCSmithPerson alloc]init];
    [smithPerson setInstanceFullName:@"Hello Jason"];
//    smithPerson.fullName=@"Hello Jason";  // crash
    
    
    smithPerson.firstName=@"Jason";
    smithPerson.lastName=@"Jason";
    
}

#pragma mark ----   测试 对象的等同性尤其是 添加了 可变对象的情况下 

/**
 测试  对象的等同性 包含了 可变对象的情况下
 */
- (void)testObjectEqualityWithinMutableObject
{
    NSMutableSet *set=[NSMutableSet set];
    
}


#pragma mark ----  在类继承体系中查询类型信息
- (void)searchClassInfoInherit
{
    NSMutableDictionary *dict=[NSMutableDictionary new];
    NSMutableDictionary *dicTwo=[NSMutableDictionary dictionary];
    
    BOOL one= [dict isMemberOfClass:[NSDictionary class]];  // NO
    BOOL two= [dict isMemberOfClass:[NSMutableDictionary class]]; // NO
    

    
    BOOL thr= [dict isKindOfClass:[NSDictionary class]];  // YES
    BOOL four= [dict isKindOfClass:[NSMutableDictionary class]]; // YES
    BOOL five= [dict isKindOfClass:[NSArray class]];  // NO
    
    NSLog(@"one:%d two:%d thr:%d four:%d five:%d",one,two,thr,four,five);
    
    
    BOOL six=[dicTwo isMemberOfClass:[NSDictionary class]];// NO
    BOOL seven=[dicTwo isMemberOfClass:[NSMutableDictionary class]]; // NO
    
    
}


#pragma mark ---- find the obj in array


- (NSString *)commaSeparatedStringFromObjects:(NSArray *)array
{
    NSMutableString *string=[NSMutableString new];
    for(id object in  array){
        if ([object isKindOfClass:[NSString class]]) {
            [string appendFormat:@"%@",object];
        }else if ([object isKindOfClass:[NSNumber class]]){
            [string appendFormat:@"%d",[object intValue]];
        }else if ([object isKindOfClass:[NSData class]]){
            NSString *base64Encode=[object base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength];
            [string appendFormat:@"%@",base64Encode];
        }
    }
    
    return string;
}

/*
  每个类对象 是
 */
#pragma mark ---- 测试重复定义的 completion 函数
- (void)testDumplicatedCompletion
{
    NSString *mp3Path=[[NSBundle mainBundle] pathForResource:@"50" ofType:@"mp3"];
    NSURL *url=[NSURL fileURLWithPath:mp3Path];
    NSLog(@"mp3  path is %@",mp3Path);
    
    EOCSoundPlayer *player=[[EOCSoundPlayer alloc]initWithUrl:url];
    player.delegate=self;
    [player playSound];
    //
    NSLog(@"play is %@",player);
    
}


#pragma mark ----  EOCPlayerDelegate

- (void)soundPlayerDidFinish:(EOCSoundPlayer *)player
{
    NSLog(@"sound paly finisd");
}

#pragma mark ----   提供 全能初始化方法 总是使用全能初始化方法 并且 维系这种体系
/*
    每个子类的全能初始化方法都应该 调用起超类的对应的方法. 并逐层向上.
    实现 "initWithCoder:" 时也要这样.应该先调用超类的相关方法然后再
    执行与本类有关的任务  apple 中NSDate 就是这样设计的一个典型
    "initWithTimeIntervalSinceRefrenceDate:"是全能初始化方法  
     也就是说其他的初始化方法都要调用它. 当底层数据存储机制改变时,只需修改
     此方法的代码就好.无需改动其他的初始化方法
     而且能 减少 消息的转发带来的消耗
 */

/*
  我们注释掉 square的 自带的 初始化 方法
  注意整个消息转发的流程
 */
- (void)testSquare
{
    EOCSquare *square=[[EOCSquare alloc]init];
    NSLog(@"square  width is:%f  heigt  is:%f ",square.width,square.height);
    
    
}

#pragma mark ----  测试 写了 description   和 debugDescription 的

/**
 我们放在 person 中来实验效果对比 po
 */
- (void)poDescription
{
    EOCPerson *person=[[EOCPerson alloc] initWithFirstName:@"Ted" lastName:@"Yang"];
    NSLog(@"person dec is %@",[person description]);
    
    NSLog(@"let po person test");
}


#pragma mark ---- 尽量使用不可变对象
/*
    使用 不可变的 对象
    例子:
    某对象所表示的对象源自于一项只读的网络服务 我们没有必要改变
    改变了也需要通知服务端
    如果可变对象放入collection 之后又修改器内容 那么容易就会破坏
    set的内部 数据结构 使其失去固有的语义
    测试使用KVC 的方式更改那些 readOnly 的数据看能否成功 实验证明是可以的
 */

- (void)userImmutableObj
{
    EOCPointOfInterest *pointOfInterest=[[EOCPointOfInterest alloc]initWithIdentifier:@"identifier" title:@"title" latitude:22.123456 longtitude:112.123456];
    NSLog(@"inset is %@",pointOfInterest.identifier);
    [pointOfInterest setValue:@"this_is_obj" forKey:@"identifier"];
    NSLog(@"inset is %@",pointOfInterest.identifier);
    
}
























@end;
